/*
  Copyright 2007 Anders Båtstrand

      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      See the License for the specific language governing permissions and
      limitations under the License.

 */
package org.dyndns.anderlin.jmplayer.client.ui;

import org.dyndns.anderlin.jmplayer.model.Instrument;
import org.dyndns.anderlin.jmplayer.service.InstrumentService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.binding.form.HierarchicalFormModel;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.richclient.application.PageComponentContext;
import org.springframework.richclient.application.event.LifecycleApplicationEvent;
import org.springframework.richclient.application.support.AbstractView;
import org.springframework.richclient.command.CommandGroup;
import org.springframework.richclient.command.support.AbstractActionCommandExecutor;
import org.springframework.richclient.command.support.GlobalCommandIds;
import org.springframework.richclient.dialog.CompositeDialogPage;
import org.springframework.richclient.dialog.ConfirmationDialog;
import org.springframework.richclient.dialog.TabbedDialogPage;
import org.springframework.richclient.dialog.TitledPageApplicationDialog;
import org.springframework.richclient.form.FormModelHelper;
import org.springframework.richclient.progress.TreeStatusBarUpdater;
import org.springframework.richclient.tree.FocusableTreeCellRenderer;
import org.springframework.richclient.util.PopupMenuMouseListener;
import org.springframework.util.Assert;

import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collection;

public class InstrumentManagerView extends AbstractView implements InitializingBean, ApplicationListener {

    private JTree instrumentTree;

    private DefaultTreeModel instrumentTreeModel;

    private InstrumentService instrumentService;

    private DeleteExecutor deleteExecutor = new DeleteExecutor();

    private PropertiesExecutor propertiesExecutor = new PropertiesExecutor();

    private InstrumentPropertiesExecutor instrumentPropertiesExecutor = new InstrumentPropertiesExecutor();

    public void setInstrumentService(InstrumentService instrumentService) {
        this.instrumentService = instrumentService;
    }

    public void afterPropertiesSet() throws Exception {
        Assert.notNull(instrumentService, "A instrumentService must be provided.");
    }

    protected JComponent createControl() {
        JPanel view = new JPanel(new BorderLayout());
        createInstrumentManagerTree();
        JScrollPane sp = new JScrollPane(instrumentTree);
        view.add(sp, BorderLayout.CENTER);
        return view;
    }

    private void createInstrumentManagerTree() {
        DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode("Instruments");
        Collection<Instrument> instruments = instrumentService.getAllInstruments();
        for (Instrument instrument : instruments) {
            DefaultMutableTreeNode instrumentNode = new DefaultMutableTreeNode(instrument);
            // This would have loaded ALL images and imageVersion. Instead loadon demand
            /* for (Image image : instrument.getImages()) {
               DefaultMutableTreeNode imageNode = new DefaultMutableTreeNode(image);
               instrumentNode.add(imageNode);
               for (ImageVersion imageVersion : image.getVersions()) {
                   imageNode.add(new DefaultMutableTreeNode(imageVersion));
               }
           } */
            rootNode.add(instrumentNode);
        }
        this.instrumentTreeModel = new DefaultTreeModel(rootNode);
        this.instrumentTree = new JTree(instrumentTreeModel);
        instrumentTree.setShowsRootHandles(true);
        instrumentTree.addTreeSelectionListener(new TreeStatusBarUpdater(getStatusBar()) {
            public String getSelectedObjectName() {
                Instrument selectedInstrument = getSelectedInstrument();
                if (selectedInstrument != null) {
                    return selectedInstrument.getName();
                }
                return "Instruments";
            }
        });
        instrumentTree.addTreeSelectionListener(new TreeSelectionListener() {
            public void valueChanged(TreeSelectionEvent e) {
                updateCommands();

                // hour-glass while fetching
                instrumentTree.setCursor(new Cursor(Cursor.WAIT_CURSOR));
                /**
                 // fetch children of the selected object
                 if (getSelectedInstrument() != null) {
                 Instrument p = getSelectedInstrument();
                 DefaultMutableTreeNode n = getSelectedInstrumentNode();
                 for (Image i : p.getImages()) {
                 n.add(new DefaultMutableTreeNode(i));
                 }
                 } else if (getSelectedImage() != null) {
                 Image p = getSelectedImage();
                 DefaultMutableTreeNode n = getSelectedImageNode();
                 for (ImageVersion i : p.getVersions()) {
                 n.add(new DefaultMutableTreeNode(i));
                 }
                 }    **/
                // finnished
                instrumentTree.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
            }
        });
        instrumentTree.addMouseListener(new PopupMenuMouseListener() {
            protected boolean onAboutToShow(MouseEvent e) {
                return !isRootOrNothingSelected();
            }

            protected JPopupMenu getPopupMenu() {
                return getSelectedInstrument() != null ? createInstrumentPopupContextMenu() : null;
            }
        });
        instrumentTree.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1 && propertiesExecutor.isEnabled()) {
                    propertiesExecutor.execute();
                }
            }
        });
        instrumentTree.setCellRenderer(getTreeCellRenderer());
        instrumentTree.setRootVisible(true);
    }

    private void updateCommands() {
        int treeSelectionCount = instrumentTree.getSelectionCount();
        if (isRootOrNothingSelected()) {
            deleteExecutor.setEnabled(false);
            propertiesExecutor.setEnabled(false);
        } else if (treeSelectionCount == 1) {
            deleteExecutor.setEnabled(true);
            propertiesExecutor.setEnabled(true);
        } else if (treeSelectionCount > 1) {
            deleteExecutor.setEnabled(true);
            propertiesExecutor.setEnabled(false);
        }
    }

    private boolean isRootOrNothingSelected() {
        return instrumentTree.getSelectionCount() == 0
                || (instrumentTree.getSelectionCount() == 1 && instrumentTree.isRowSelected(0));
    }

    private Instrument getSelectedInstrument() {
        DefaultMutableTreeNode node = getSelectedInstrumentNode();
        if (node != null)
            return (Instrument) node.getUserObject();

        return null;
    }

    private DefaultMutableTreeNode getSelectedInstrumentNode() {
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) instrumentTree.getLastSelectedPathComponent();
        if (node == null || !(node.getUserObject() instanceof Instrument))
            return null;

        return node;
    }

    private DefaultTreeCellRenderer treeCellRenderer = new FocusableTreeCellRenderer() {
        public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded,
                                                      boolean leaf, int row, boolean hasFocus) {
            super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
            if (node.isRoot()) {
                this.setIcon(getIconSource().getIcon("folder.icon"));
            } else if (node.getUserObject() instanceof Instrument) {
                Instrument instrument = (Instrument) node.getUserObject();
                this.setText(instrument.getName());
                this.setIcon(getIconSource().getIcon("instrument.bullet"));
            }
            return this;
        }
    };

    public TreeCellRenderer getTreeCellRenderer() {
        return treeCellRenderer;
    }

    protected void registerLocalCommandExecutors(PageComponentContext context) {
        context.register(GlobalCommandIds.DELETE, deleteExecutor);
        context.register(GlobalCommandIds.PROPERTIES, propertiesExecutor);
    }

    /**
     * Handle an application event.
     *
     * @param e the event to respond to
     */

    public void onApplicationEvent(ApplicationEvent e) {
        if (e instanceof LifecycleApplicationEvent) {
            LifecycleApplicationEvent le = (LifecycleApplicationEvent) e;
            if (le.getEventType().equals(LifecycleApplicationEvent.CREATED) && le.objectIs(Instrument.class)) {
                if (instrumentTree != null) {
                    DefaultMutableTreeNode root = (DefaultMutableTreeNode) instrumentTreeModel.getRoot();
                    root.add(new DefaultMutableTreeNode(le.getObject()));
                    instrumentTreeModel.nodeStructureChanged(root);
                }
            }
        }
    }

    private JPopupMenu createInstrumentPopupContextMenu() {
        // delete, addImage separator, properties
        CommandGroup group = getWindowCommandManager().createCommandGroup(
                "instrumentViewTableInstrumentCommandGroup",
                new Object[]{"deleteCommand", "separator",
                        "propertiesCommand"});
        return group.createPopupMenu();
    }

    private class PropertiesExecutor extends AbstractActionCommandExecutor {
        public void execute() {
            if (getSelectedInstrument() != null) {
                instrumentPropertiesExecutor.execute();
            }
        }
    }

    private class DeleteExecutor extends AbstractActionCommandExecutor {
        public void execute() {
            int instrumentCount = 0;
            int imageCount = 0;
            int imageVersionCount = 0;
            final java.util.List<DefaultMutableTreeNode> nodesToDelete = new ArrayList<DefaultMutableTreeNode>();
            TreePath[] paths = instrumentTree.getSelectionPaths();
            for (TreePath path : paths) {
                DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
                if (node.isRoot()) {
                    continue;
                }
                if (node.getUserObject() instanceof Instrument) {
                    instrumentCount++;
                }
                nodesToDelete.add(node);
            }

            ConfirmationDialog dialog = new ConfirmationDialog() {
                protected void onConfirm() {
                    for (DefaultMutableTreeNode node : nodesToDelete) {
                        instrumentTreeModel.removeNodeFromParent(node);
                        if (node.getUserObject() instanceof Instrument) {
                            instrumentService.remove((Instrument) node.getUserObject());
                        }
                    }
                }
            };
            if (instrumentCount > 0) {
                dialog.setTitle(getMessage("confirmDeleteInstrumentDialog.title"));
                dialog.setConfirmationMessage(getMessage("confirmDeleteInstrumentDialog.label"));
            } else if (imageCount > 0) {
                dialog.setTitle(getMessage("confirmDeleteImageDialog.title"));
                dialog.setConfirmationMessage(getMessage("confirmDeleteImageDialog.label"));
            } else if (imageVersionCount > 0) {
                dialog.setTitle(getMessage("confirmDeleteImageVersionDialog.title"));
                dialog.setConfirmationMessage(getMessage("confirmDeleteImageVersionDialog.label"));
            }

            dialog.showDialog();
        }
    }

    // TODO: Move all usefull classes out of this mess!
    private class InstrumentPropertiesExecutor extends AbstractActionCommandExecutor {

        private HierarchicalFormModel instrumentFormModel;

        private InstrumentForm instrumentForm;

        private CompositeDialogPage compositePage;

        public void execute() {
            final Instrument instrument = getSelectedInstrument();
            instrumentFormModel = FormModelHelper.createCompoundFormModel(instrument);
            instrumentForm = new InstrumentForm(FormModelHelper.createChildPageFormModel(instrumentFormModel, null));

            compositePage = new TabbedDialogPage("instrumentProperties");
            compositePage.addForm(instrumentForm);

            TitledPageApplicationDialog dialog = new TitledPageApplicationDialog(compositePage, getWindowControl()) {
                protected void onAboutToShow() {
                    instrumentForm.requestFocusInWindow();
                    setEnabled(compositePage.isPageComplete());
                }

                protected boolean onFinish() {
                    instrumentFormModel.commit();
                    instrumentService.saveOrUpdate(instrument);
                    instrumentTreeModel.nodeChanged(getSelectedInstrumentNode());
                    return true;
                }
            };
            dialog.showDialog();
        }
    }

}